<!--
 * @Description: 树结构选择
 * @Author: HuiSir<273250950@qq.com>
 * @Date: 2021-12-24 10:51:13
 * @LastEditTime: 2025-07-31 14:57:28
-->
<template>
  <el-popover
    :visible="visible"
    popper-class="tree-select"
    placement="bottom-start"
    :width="220"
    @show="show"
  >
    <template #reference>
      <el-input
        class="tree-select-input"
        v-model="inputLabel"
        @input="emit('update:label', $event)"
        readonly
        placeholder="请选择"
        @click="visible = !visible"
      >
        <template #suffix>
          <fy-icon
            @click="visible = !visible"
            name="ArrowUp"
            color="var(--el-select-input-color)"
            size="14"
            :style="{
              transition: 'transform var(--el-transition-duration)',
              transform: iconRotate,
              cursor: 'pointer',
            }"
          />
        </template>
      </el-input>
    </template>
    <el-input
      class="search-input"
      v-model="filterText"
      clearable
      placeholder="搜索..."
      @input="searchInputChange"
    />
    <div class="tree-box">
      <el-tree
        ref="tree"
        class="filter-tree"
        :data="data"
        :props="option"
        :filter-node-method="filterNode"
        node-key="id"
        highlight-current
        @current-change="bindCurrChange"
      />
    </div>
  </el-popover>
</template>

<script lang="ts" setup>
import { computed, PropType, ref, watch, nextTick } from "vue";
import { clickOtherPosToClose } from "../../helpers";

// 组件名称（必要）
defineOptions({ name: "FyTreeSelect" });

const selectVal = defineModel<string>();

const inputLabel = ref("");

const props = defineProps({
  data: {
    type: Object,
    required: true,
  },
  option: {
    type: Object as PropType<{
      children: string;
      label: string;
      disabled?: (data: unknown, node: unknown) => boolean;
    }>,
    default: () => ({
      children: "children",
      label: "label",
    }),
  },
});

const emit = defineEmits(["update:label"]);

// 弹窗显隐
const visible = ref(false);

// 树ref
const tree = ref();

// 搜索节点
const filterText = ref("");

// 輸入框圖標反轉
const iconRotate = computed(() =>
  visible.value ? "rotateZ(0)" : "rotateZ(180deg)"
);

// 点选
const bindCurrChange = (data: { [x: string]: string; id: string }) => {
  selectVal.value = data.id;
  inputLabel.value = data[props.option.label];
};

watch(
  selectVal,
  (val) => {
    nextTick(() => {
      tree.value.setCurrentKey(val);
    });
  },
  { immediate: true }
);

// 显隐事件
const show = () => {
  // 置空搜索
  filterText.value = "";
  searchInputChange("");
  // 点击空白关闭下拉
  const inputEl = document.querySelector(".tree-select-input");
  const popEl = document.querySelector(".el-popover.tree-select");
  clickOtherPosToClose([inputEl as HTMLElement, popEl as HTMLElement], () => {
    visible.value = false;
  });
};

// 查询
const searchInputChange = (val: string) => {
  tree.value.filter(val);
};
const filterNode = (value: string, data: { title: string | string[] }) => {
  if (!value) return true;
  return data.title.indexOf(value) !== -1;
};

// 当前所选数据
const getCurrentNode = () => tree.value.getCurrentNode();

defineExpose({
  searchInputChange,
  getCurrentNode,
  inputLabel,
  tree,
});
</script>

<style scoped lang="scss">
@use "../../styles/mixin.scss" as *;

.tree-box {
  max-height: 210px;
  overflow: auto;
  padding-right: 5px;
  @include scrollBar;
}
</style>
<style lang="scss">
.el-popover.tree-select {
  padding: 10px 8px 10px 10px;

  .search-input {
    width: calc(100% - 2px);
    margin-bottom: 5px;
  }
}

.tree-select-input .el-input__inner {
  cursor: pointer;
}
</style>
